package dataExchange

import (
	"fmt"
	"gitee.com/kingzyt/common/util"
	"sync"
	"time"

	"gitee.com/kingzyt/common/log"
)

var (
	ErrDataExchangeDealIsNotExisted = fmt.Errorf("deal isnot existed")
	ErrDataExchangeDealIsTimeout    = fmt.Errorf("deal is timeout")
	ErrDataExchangeChanIsClosed     = fmt.Errorf("chan is closed")
)

type DataExchange struct {
	nextId       util.AtomicInt64
	datasForSale map[int64]interface{}
	paymentChans map[int64]chan interface{}
	mutex        sync.Mutex
}

func NewDataExchange() *DataExchange {
	return &DataExchange{
		datasForSale: map[int64]interface{}{},
		paymentChans: map[int64]chan interface{}{},
	}
}

func (self *DataExchange) SellData(dataForSale interface{}) (id int64) {
	ch := make(chan interface{}, 1)
	id = self.nextId.Add(1)

	self.mutex.Lock()
	defer self.mutex.Unlock()
	self.datasForSale[id] = dataForSale
	self.paymentChans[id] = ch
	return id
}

func (self *DataExchange) GetPaymentData(id int64, timeout time.Duration) (dataForPay interface{}, err error) {
	var paidCh <-chan interface{}
	err = func() error {
		self.mutex.Lock()
		defer self.mutex.Unlock()
		ch, ok := self.paymentChans[id]
		if !ok {
			log.Warn("DataExchange", "deal isnot existed, id:%d", id)
			return ErrDataExchangeDealIsNotExisted
		}
		paidCh = ch
		return nil
	}()
	if err != nil {
		return
	}

	defer func() {
		self.mutex.Lock()
		defer self.mutex.Unlock()
		//保证释放所有资源
		delete(self.datasForSale, id)
		delete(self.paymentChans, id)
	}()

	timeouter := time.NewTimer(timeout)
	defer timeouter.Stop()
	select {
	case <-timeouter.C:
		log.Warn("DataExchange", "deal is timeout, id:%d", id)
		return nil, ErrDataExchangeDealIsTimeout
	case dataForPay, ok := <-paidCh:
		if !ok {
			log.Warn("DataExchange", "chan is closed, id:%d", id)
			return nil, ErrDataExchangeChanIsClosed
		}
		return dataForPay, nil
	}
}

func (self *DataExchange) BuyData(id int64, exchange func(dataForSale interface{}) (dataForPay interface{})) (err error) {
	var waitPayCh chan<- interface{}
	var dataForSale interface{}

	err = func() error {
		self.mutex.Lock()
		defer self.mutex.Unlock()
		data, ok := self.datasForSale[id]
		if !ok {
			log.Warn("DataExchange", "deal isnot existed/has been paid/is timeout, no data, id:%d", id)
			return ErrDataExchangeDealIsNotExisted
		}
		//作为交换完成的标记
		delete(self.datasForSale, id)
		dataForSale = data
		ch, ok := self.paymentChans[id]
		if !ok {
			log.Warn("DataExchange", "deal has been timeout, no chan, id:%d", id)
			return ErrDataExchangeDealIsTimeout
		}
		waitPayCh = ch
		return nil
	}()
	if err != nil {
		return
	}

	waitPayCh <- exchange(dataForSale)
	close(waitPayCh)

	return nil
}
